﻿using System;
using System.Collections.Generic;
using System.Text;
using EnCor.Hosting;
using System.ServiceProcess;
using EnCor.Wcf.Hosting;
using System.ServiceModel;
using EnCor.ObjectBuilder;
using EnCor.Logging;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;

namespace EnCor.Wcf
{
    [AssembleConfig(typeof(WcfHostConfig))]
    public class WcfHost : FacadeHost
    {
        private ILog _Logger = LogManager.GetLogger("EnCor.Hosting");

        private ServiceHost _RouterHost;

        private ServiceHost[] _ActualServiceHosts;

        private List<WcfRoutingInfo> _RoutingMap = new List<WcfRoutingInfo>();

        private Uri _RouterUri;

        private Uri _ServiceBaseUri;

        private IEnumerable<FacadeServiceConfig> _ServiceConfigCollection;

        public WcfHost(Uri routerUri, Uri serviceBaseUri, IEnumerable<FacadeServiceConfig> serviceConfigCollection)
        {
            _RouterUri = routerUri;
            _ServiceBaseUri = serviceBaseUri;
            _ServiceConfigCollection = serviceConfigCollection;
        }

        public override void Start()
        {
            try
            {
                StartActualServices();

                StartRouterService();
            }
            catch (Exception ex)
            {
                //
                if (_RouterHost != null)
                {
                    _RouterHost.Close();
                    _RouterHost = null;
                }

                if (_ActualServiceHosts != null)
                {
                    foreach (ServiceHost host in _ActualServiceHosts)
                    {
                        host.Close();
                    }
                    _ActualServiceHosts = null;
                }
                _Logger.Error("Host starting failed.", ex);
                throw;
            }

        }

        private ServiceHost StartRouterService()
        {
            
            //RouterService router = new RouterService(_RoutingMap);
            //ServiceHost host = new ServiceHost(router, _RouterUri);
            //Binding serviceEndpointBinding;

            //switch (_RouterUri.Scheme)
            //{
            //    case "http":
            //    case "https":
            //        serviceEndpointBinding = new BasicHttpBinding();
            //        break;
            //    case "net.tcp":
            //        serviceEndpointBinding = new NetTcpBinding();
            //        break;
            //    default:
            //        throw new NotSupportedException(string.Format("The schema {0} is not supported now.", _RouterUri.Scheme));
            //}

            //ServiceEndpoint serviceEndpoint = host.AddServiceEndpoint(typeof(IRouterService), serviceEndpointBinding, _RouterUri);
            
            //host.Open();
            //_Logger.Info(string.Format("Start WCF Routing Service on {0}", serviceEndpoint.ListenUri));


            //return host;
            throw new NotImplementedException();
        }

        private ServiceHost[] StartActualServices()
        {
            List<ServiceHost> hostList = new List<ServiceHost>();
            foreach (FacadeServiceConfig serviceConfig in  _ServiceConfigCollection)
            {
                Uri serviceEndpointUri = new Uri(_ServiceBaseUri, serviceConfig.Name);


                ServiceHost host = new ServiceHost(serviceConfig.Type, serviceEndpointUri);
                ServiceEndpoint serviceEndpoint = null;
                Binding basicBinding = null;
                NetTcpBinding tcpBinding = null;

                switch (_ServiceBaseUri.Scheme)
                { 
                    case "http":
                    case "https":
                        basicBinding = new BasicHttpBinding();
                        serviceEndpoint = host.AddServiceEndpoint(serviceConfig.Type, basicBinding, serviceEndpointUri);
                        break;
                    case "net.tcp":
                        tcpBinding = new System.ServiceModel.NetTcpBinding(SecurityMode.None);
                        tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.None;
                        serviceEndpoint = host.AddServiceEndpoint(serviceConfig.Type, tcpBinding, serviceEndpointUri);
                        break;
                    default:
                        throw new NotSupportedException(string.Format("The schema {0} is not supported now.", _ServiceBaseUri.Scheme));
                }
                

                string serviceAction = serviceEndpoint.Contract.Namespace + "/" + serviceEndpoint.Contract.Name;
                WcfRoutingInfo routingInfo = new WcfRoutingInfo
                {
                    ServiceName = serviceConfig.Name,
                    Url = serviceEndpoint.ListenUri.ToString(),
                    Action = serviceAction,
                    Binding = basicBinding == null ? tcpBinding : basicBinding
                };

                _RoutingMap.Add(routingInfo);
                ServiceMetadataBehavior metadataBehavior;
                metadataBehavior =
                   host.Description.Behaviors.
                   Find<ServiceMetadataBehavior>();
                if (metadataBehavior == null)
                {
                    metadataBehavior = new ServiceMetadataBehavior();
                    if (_ServiceBaseUri.Scheme == Uri.UriSchemeHttp || _ServiceBaseUri.Scheme == Uri.UriSchemeHttps)
                    {
                        metadataBehavior.HttpGetEnabled = true;
                    }
                    host.Description.Behaviors.Add(metadataBehavior);
                }

                ServiceDebugBehavior debugBehavior;
                debugBehavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
                if (debugBehavior == null)
                {
                    debugBehavior = new ServiceDebugBehavior();
                    host.Description.Behaviors.Add(debugBehavior);
                }
                debugBehavior.IncludeExceptionDetailInFaults = true;

                BindingElement metaDataBindingElement = null;
                switch (_ServiceBaseUri.Scheme)
                {
                    case "http":
                    case "https":
                        metaDataBindingElement = new HttpTransportBindingElement();
                        break;
                    case "net.tcp":
                        metaDataBindingElement = new TcpTransportBindingElement();
                        break;
                    default:
                        throw new NotSupportedException(string.Format("The schema {0} is not supported now.", _ServiceBaseUri.Scheme));
                }
                CustomBinding binding = new CustomBinding(metaDataBindingElement);
                host.AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");

                host.Open();
                _Logger.Info(string.Format("Start WCF Service {0} on {1}", serviceConfig.Name, serviceEndpoint.ListenUri));
                hostList.Add(host);
            }

            return hostList.ToArray();
        }

        public override void Stop()
        {
            if (_RouterHost != null)
            {
                _RouterHost.Close();
                _RouterHost = null;
            }

            if (_ActualServiceHosts != null)
            {
                foreach (ServiceHost host in _ActualServiceHosts)
                {
                    host.Close();
                }
                _ActualServiceHosts = null;
            }
        }
    }
}
